<?php

namespace app\common\model\banxi\core;

use app\common\enums\TrainerTimeStatusEnum;
use app\common\model\common\BaseModel;
use think\exception\ValidateException;
use think\facade\Cache;
use think\model\concern\SoftDelete;

/**
 * 账户课时表
 */
class CoreTrainerTime extends BaseModel
{
    use SoftDelete;

    protected $name = 'banxi_core_trainer_time';

    protected $autoWriteTimestamp = true;

    protected $deleteTime = 'del';

    protected $defaultSoftDelete = 0;

    protected $append = [
        "statusCn"
    ];

    protected $type = [
        'id' => 'string',
        'del' => 'string',
        'trainerId' => 'string',
        'createTime' => 'timestamp:Y-m-d H:i:s',
        'updateTime' => 'timestamp:Y-m-d H:i:s',
    ];

    //-----------------------------------------------------------------------------------
    //｜                                   获取器                                       ｜
    //-----------------------------------------------------------------------------------

    public function getStatusCnAttr($value, $data)
    {
        $value = $value ?: ($data['status'] ?? "");
        return (new TrainerTimeStatusEnum())->getDescription($value) ?? '';
    }

    public function getStartTimeAttr($value, $data)
    {
        $value = $value ?: ($data['startTime'] ?? "");
        return date('H:i', strtotime($value));
    }

    public function getEndTimeAttr($value, $data)
    {
        $value = $value ?: ($data['endTime'] ?? "");
        return date('H:i', strtotime($value));
    }
    //-----------------------------------------------------------------------------------
    //｜                                   处理方法                                       ｜
    //-----------------------------------------------------------------------------------

    public function listByTrainerAndDate($trainerIds, $date)
    {
        return $this->whereIn('trainerId', $trainerIds)
            ->where('date', $date)
            ->order(['date' => 'asc', 'startTime' => 'asc'])
            ->select();
    }

    public function lockTrainers($trainerList)
    {
        $result = [];
        try {
            foreach ($trainerList as $trainer) {
                $lock = $this->lockTrainer($trainer);
                if ($lock) {
                    $result[] = $lock;
                } else {
                    throw new ValidateException("教练{$trainer['name']}正在排课中，请稍后重试");
                }
            }
        } finally {
            foreach ($result as $item) {
                Cache::delete($item);
            }
        }
        return $result;
    }

    public function lockTrainer($trainer)
    {
        $key = "CoreTrainerTime:trainer_time:{$trainer['id']}";
        $value = uniqid(); // 随机值，防止误删其他线程的锁
        $expire = 5; // 锁的过期时间（秒）
        // 使用 Redis SETNX 命令尝试获取锁
        $success = Cache::set($key, $value, $expire);

        return $success ? $key : null;
    }

    public function listNotIdleByTrainerAndDateIn($trainerIds, $dateList)
    {
        return $this->where([
            ['trainerId', 'in', $trainerIds],
            ['date', 'in', $dateList],
            ['status', '<>', TrainerTimeStatusEnum::IDLE]
        ])->order(['date' => 'asc', 'startTime' => 'asc'])->select()->toArray();
    }

    public function getPlanTimeIdle($timeList)
    {
        if (empty($timeList)) {
            return [];
        }
        foreach ($timeList as $time) {
            $startTime = $time['startTime'];
            $endTime = $time['endTime'];

            // 简单字符串比较（假设输入格式正确）
            if ($startTime == $endTime) {
                throw new ValidateException("开始时间和结束时间不能相同 {$startTime}");
            }

            // 处理跨天情况（结束时间为00:00表示第二天）
            if ($startTime > $endTime && $endTime != '00:00') {
                throw new ValidateException("开始时间不能大于结束时间 {$startTime}~{$endTime}");
            }

        }
        usort($timeList, function ($a, $b) {
            $startTimeA = $a['startTime'];
            $startTimeB = $b['startTime'];

            if ($startTimeA == $startTimeB) {
                return 0;
            }
            return ($startTimeA < $startTimeB) ? -1 : 1;
        });
        $resultList = [];
        $currentTimeStatus = TrainerTimeStatusEnum::IDLE;
        $currentTime = '00:00:00';
        $currentTrainerTime = null;
        $lastTime = '23:59:59';
        foreach ($timeList as $time) {
            $startTime = normalizeAndCompare($time['startTime']);
            $endTime = normalizeAndCompare($time['endTime']);
            $timeStatus = $time['status'];
            // 和当前时间不冲突的时段
            if ($currentTime < $startTime) {
                $addTime['trainerId'] = $time['trainerId'];
                $addTime['date'] = $time['date'];
                $addTime['startTime'] = $currentTime;
                $addTime['endTime'] = $startTime;
                $addTime['status'] = TrainerTimeStatusEnum::IDLE;
                $resultList[] = $addTime;
            } elseif ($currentTime > $startTime) {
                // 和当前时间冲突的时间
                // 如果两个时间都不为空闲时间，抛出异常
                if ($currentTimeStatus != TrainerTimeStatusEnum::IDLE && $timeStatus != TrainerTimeStatusEnum::IDLE) {
                    $msg = $this->strifeMsg($currentTrainerTime, $time);
                    throw new ValidateException($msg);
                }
                // 如果被压缩的时间是预约时间，抛出异常
                if ($currentTimeStatus == TrainerTimeStatusEnum::APPOINTMENT) {
                    $msg = $this->strifeMsg($currentTrainerTime, $time);
                    throw new ValidateException($msg);
                }

                // 挤压当前时间段
                if ($currentTrainerTime != null) {
                    $currentTrainerTime['endTime'] = $startTime;
                }
            }

            // 更新当前时间状态和时间
            $currentTimeStatus = $timeStatus;
            $currentTime = $endTime;
            $currentTrainerTime = $time;
            // 将当前时间段加入结果列表
            $resultList[] = $time;
        }

        $lastTimePlan = end($resultList);
        $lastTimePlanEndTime = normalizeAndCompare($lastTimePlan['endTime']);
        // 剩余空余时间计算
        if ($lastTimePlanEndTime < $lastTime) {
            $addTime['trainerId'] = $time['trainerId'];
            $addTime['date'] = $time['date'];
            $addTime['startTime'] = $currentTime;
            $addTime['endTime'] = $lastTime;
            $addTime['status'] = TrainerTimeStatusEnum::IDLE;
            $resultList[] = $addTime;
        }
        // 分离空闲和非空闲时间
        $result = array_values(array_filter($resultList, function ($it) {
            return $it['status'] != TrainerTimeStatusEnum::IDLE;
        }));

        $idleTimeList = array_values(array_filter($resultList, function ($it) {
            return $it['status'] == TrainerTimeStatusEnum::IDLE;
        }));

        // 合并空闲时间段
        $result = $this->mergeTime($result, $idleTimeList);
        // 按开始时间排序
        usort($result, function ($a, $b) {
            return strcmp($a['startTime'], $b['startTime']); // 按字典顺序比较时间字符串
        });
        return $result;


    }

    public function strifeMsg($currentTime, $nextTime)
    {
        $date = $nextTime['date'];
        $startTime = $nextTime['startTime'];
        $endTime = $nextTime['endTime'];
        if (!$currentTime) {
            $msg = "日期：{$date} 时间段{$startTime}~{$endTime}冲突";
        } else {
            $currentTimeStatus = $currentTime['status'];
            $currentTimeStartTime = $currentTime['startTime'];
            $currentTimeEndTime = $currentTime['endTime'];
            $msg = $currentTimeStatus == TrainerTimeStatusEnum::APPOINTMENT ? "预约" : "休息";
            $msg = "日期：{$date} 时间段{$startTime}~{$endTime}占用了【{$msg}】时间 【{$msg}{$currentTimeStartTime}~{$currentTimeEndTime}】";
        }
        return $msg;
    }

    public function mergeTime($result, $timeList)
    {
        if (empty($timeList)) {
            return $result;
        }
        $currentTime = $timeList[0];
        $currentLastTime = $currentTime['endTime'];
        for ($i = 1; $i < count($timeList); $i++) {
            $nextTime = $timeList[$i];
            $startTime = $nextTime['startTime'];
            if ($currentLastTime == $startTime) {
                $currentLastTime = $nextTime['endTime'];
            } else {
                $currentTime['endTime'] = $currentLastTime;
                $result[] = $currentTime;
                $currentTime = $nextTime;
                $currentLastTime = $nextTime['endTime'];
            }
        }
        $currentTime['endTime'] = $currentLastTime;
        $result[] = $currentTime;
        return $result;
    }

    public function removeByIdleAndTrainerAndDateIn($trainerIds, $dateList)
    {
        $where = [
            ['trainerId', 'in', $trainerIds],
            ['date', 'in', $dateList],
            ['status', '=', TrainerTimeStatusEnum::IDLE],
        ];
        return CoreTrainerTime::destroy(function ($query) use ($where) {
            $query->where($where);
        });

    }

    public function removeByIdleAndTrainerAndDateAndTime($trainerId, $date, $startTime, $endTime)
    {
        return self::destroy(function ($query) use ($trainerId, $date, $startTime, $endTime) {
            $query->where('trainerId', $trainerId)
                ->where('date', $date)
                ->where(function ($subQuery) use ($startTime, $endTime) {
                    $subQuery->where('startTime', $startTime)
                        ->whereOr([
                            'endTime' => $endTime,
                            'status' => TrainerTimeStatusEnum::IDLE
                        ]);
                });
        });
    }
    //-----------------------------------------------------------------------------------
    //｜                                   模型关联                                       ｜
    //-----------------------------------------------------------------------------------


}